package com.example.servicelearn;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class LogManager {

    public static final String TAGPENG="TAGPENG";

    public static boolean isShowLog = true;

    private static final String GLOBAL="GLOBAL";
    private static SimpleDateFormat mSdf = new SimpleDateFormat("yyyyMMdd");
    public static void writeLog(Context context) {
        try {

            String path= Environment.getExternalStorageDirectory()
                    .getAbsolutePath()+"/Test/";
            LogManager.e("log_","path="+path);
            File file = new File(path);
            if(!file.exists()){
                //noinspection ResultOfMethodCallIgnored
                file.mkdirs();
            }
            String logPath = path + "log_"
                    + mSdf.format(System.currentTimeMillis()) + ".txt";
            Runtime.getRuntime().exec("logcat -v long -f " + logPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    private static final class FilePrintRunnableError implements Runnable {

        private static PrintWriter printWriter = null;

        private int level;
        private String tag;
        private String msg;
        private Throwable t;


        private FilePrintRunnableError(int level, String tag, String msg, Throwable t) {
            this.level = level;
            this.tag = tag;
            this.msg = msg;
            this.t = t;
        }

        @Override
        public void run() {

            boolean exception = false;

            try {

                if (null == printWriter) {
                    String path= Environment.getExternalStorageDirectory().getAbsolutePath();
                    File file = new File(path+"/Test");
                    if(!file.exists()){
                        file.mkdir();
                    }
                    File logFile = new File(file.getAbsolutePath()+ File.separator + "yantuerrlog.log");
                    printWriter = new PrintWriter(new FileOutputStream(logFile, true));
                }

                // 级别
                printWriter.print("|");
                printWriter.print(levelName(level));

                // 时间
                printWriter.print("|");
                printWriter.print(TIME_FORMAT.format(new Date()));

                // TAG
                printWriter.print("|");
                printWriter.print(tag);

                // 信息
                printWriter.print("|");
                printWriter.print(msg);

                // 异常
                if (null != t) {
                    t.printStackTrace(printWriter);
                }

                printWriter.println();
                printWriter.flush();

            } catch (IOException e) {
                e.printStackTrace();
                exception = true;
            }

            if (workQueue.size() < 1 || exception) {
                if (null != printWriter) {
                    printWriter.close();
                    printWriter = null;
                }
            }

        }
    }

    private static final class FilePrintRunnableLog implements Runnable {

        private static PrintWriter printWriter = null;

        private int level;
        private String tag;
        private String msg;
        private Throwable t;


        private FilePrintRunnableLog(int level, String tag, String msg, Throwable t) {
            this.level = level;
            this.tag = tag;
            this.msg = msg;
            this.t = t;
        }

        @Override
        public void run() {

            boolean exception = false;

            try {

                if (null == printWriter) {
                    String path= Environment.getExternalStorageDirectory().getAbsolutePath();
                    File file = new File(path+"/Test");
                    if(!file.exists()){
                        file.mkdir();
                    }
                    File logFile = new File(file.getAbsolutePath()+ File.separator + "yantulog.log");
                    printWriter = new PrintWriter(new FileOutputStream(logFile, true));
                }

                String str = "";
//                if(WeNetConfigs.isEncryptLog){
                if(false){
                    try {
                        String msg1 = "|"+levelName(level)+"|"+TIME_FORMAT.format(new Date())+"|"+tag+"|"+msg;
                        byte[] enncryptBytes= DataUtils.xoEncrypt(msg1.getBytes(), "sign");
                        str= Base64Util.getEncoder().encodeToString(enncryptBytes);
                        printWriter.print(str);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }else{
                    // 级别
                    printWriter.print("|");
                    printWriter.print(levelName(level));

                    // 时间
                    printWriter.print("|");
                    printWriter.print(TIME_FORMAT.format(new Date()));

                    // TAG
                    printWriter.print("|");
                    printWriter.print(tag);

                    // 信息
                    printWriter.print("|");
                    printWriter.print(msg);
                }

                // 异常
                if (null != t) {
                    t.printStackTrace(printWriter);
                }

                printWriter.println();
                printWriter.flush();

            } catch (IOException e) {
                e.printStackTrace();
                exception = true;
            }

            if (workQueue.size() < 1 || exception) {
                if (null != printWriter) {
                    printWriter.close();
                    printWriter = null;
                }
            }

        }
    }

    public enum CodeLocationStyle {

        /**
         * 第一行
         */
        FIRST(true, true),
        /**
         * 随后的行
         */
        SUBSEQUENT(true, true);

        /**
         * 是否添加at字眼在行首
         */
        private boolean isAt;

        /**
         * 是否使用简单类名
         */
        private boolean isSimpleClassName;

        private CodeLocationStyle(boolean isAt, boolean isSimpleClassName) {
            this.isAt = isAt;
            this.isSimpleClassName = isSimpleClassName;
        }

        /**
         * @return the {@link #isAt}
         */
        public boolean isAt() {
            return isAt;
        }

        /**
         * @return the {@link #isSimpleClassName}
         */
        public boolean isSimpleClassName() {
            return isSimpleClassName;
        }

    }

    private static BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();
    private static ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, workQueue, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
//			return new Thread(r,"WeNet_LogManager_executorService");
            return new Thread(r);
        }
    });


    /**
     * 日志级别
     */
    public static final int VERBOSE = 1;
    /**
     * 日志级别
     */
    public static final int DEBUG = 2;
    /**
     * 日志级别
     */
    public static final int INFO = 3;
    /**
     * 日志级别
     */
    public static final int WARN = 4;
    /**
     * 日志级别
     */
    public static final int ERROR = 5;

    /**
     * 时间格式
     */
    public static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");

    private static void printLog(int level, String tag, String msg, Throwable t, boolean printStackTrace) {
        if (!isLoggable(tag, level)) {
            return;
        }

        Thread currentThread = Thread.currentThread();
        StackTraceElement[] stackTrace = currentThread.getStackTrace();

        int i = 4;

        StringBuilder sb = new StringBuilder();
        sb.append(currentThread.getId())//
                .append("|")//
                .append(getCodeLocation(CodeLocationStyle.FIRST, null, stackTrace[i]))//
                .append("|")//
                .append(msg);

        String msgResult = sb.toString();

        if(isShowLog){
            print(level, tag, msgResult, t);
        }
        filePrintLog(level, tag, msgResult, t);
    }

    private static void printError(int level, String tag, String msg, Throwable t, boolean printStackTrace) {
        Thread currentThread = Thread.currentThread();
        StackTraceElement[] stackTrace = currentThread.getStackTrace();

        int i = 4;

        StringBuilder sb = new StringBuilder();
        sb.append(currentThread.getId())//
                .append("|")//
                .append(getCodeLocation(CodeLocationStyle.FIRST, null, stackTrace[i]))//
                .append("|")//
                .append(msg);

        String msgResult = sb.toString();
        print(level, tag, msgResult, t);
        filePrintError(level, tag, msgResult, t);

        i++;

        for (; printStackTrace && i < stackTrace.length; i++) {
            String s = getCodeLocation(CodeLocationStyle.SUBSEQUENT, currentThread, stackTrace[i]).toString();
            print(level, tag, s, null);
            filePrintError(level, tag, s, null);
        }

    }

    private static void print(int level, String tag, String msg, Throwable t) {
        switch (level) {
            case VERBOSE:
                if (t != null) {
                    Log.v(tag, msg, t);
                } else {
                    Log.v(tag, msg);
                }
                break;
            case DEBUG:
                if (t != null) {
                    Log.d(tag, msg, t);
                } else {
                    Log.d(tag, msg);
                }
                break;
            case INFO:
                if (t != null) {
                    Log.i(tag, msg, t);
                } else {
                    Log.i(tag, msg);
                }
                break;
            case WARN:
                if (t != null) {
                    Log.w(tag, msg, t);
                } else {
                    Log.w(tag, msg);
                }
                break;
            case ERROR:
                if (t != null) {
                    Log.e(tag, msg, t);
                } else {
                    Log.e(tag, msg);
                }
                break;
            default:
                break;
        }
    }

    private static StringBuilder getCodeLocation(CodeLocationStyle style, Thread currentThread, StackTraceElement stackTraceElement) {
        String className = stackTraceElement.getClassName();
        int lineNumber = stackTraceElement.getLineNumber();
        String methodName = stackTraceElement.getMethodName();
        String fileName = stackTraceElement.getFileName();
        StringBuilder sb = new StringBuilder();
        if (style.isAt()) {
            sb.append("	at ");
        }
        if (style.isSimpleClassName()) {
            sb.append(getSimpleName(className));
        } else {
            sb.append(className);
        }
        sb.append(".").append(methodName).append("(").append(fileName).append(":").append(lineNumber).append(")");
        return sb;
    }

    private static String getSimpleName(String className) {
        String[] split = className.split("\\.");
        return split[split.length - 1];
    }

    /**
     * 是否可以打印日志
     *
     * @param tag
     * @param level
     * @return
     */
    public static boolean isLoggable(String tag, int level) {
        return isShowLog;
    }

    public static void v(String tag, String msg) {
        printLog(VERBOSE, tag, msg, null, false);
    }

    public static void v(String tag, String msg, Throwable t) {
        printLog(VERBOSE, tag, msg, t, false);
    }

    public static void vs(String tag, String msg) {
        printLog(VERBOSE, tag, msg, null, true);
    }

    public static void d(String tag, String msg) {
        printLog(DEBUG, tag, msg, null, false);
    }

    public static void d(String tag, String msg, Throwable t) {
        printLog(DEBUG, tag, msg, t, false);
    }

    public static void ds(String tag, String msg) {
        printLog(DEBUG, tag, msg, null, true);
    }

    public static void i(String tag, String msg) {
        printLog(INFO, tag, msg, null, false);
    }

    public static void i(String tag, String msg, Throwable t) {
        printLog(INFO, tag, msg, t, false);
    }

    public static void is(String tag, String msg) {
        printLog(INFO, tag, msg, null, true);
    }

    public static void w(String tag, String msg) {
        printLog(WARN, tag, msg, null, false);
    }

    public static void w(String tag, String msg, Throwable t) {
        printLog(WARN, tag, msg, t, false);
    }

    public static void ws(String tag, String msg) {
        printLog(WARN, tag, msg, null, true);
    }

    public static void e( String msg) {
        e(TAGPENG,msg);
    }

    public static void e(String tag, String msg) {
        printLog(ERROR, tag, msg, null, false);
    }

    public static void e(String tag, String msg, Throwable t) {
        printLog(ERROR, tag, msg, t, false);
    }


    public static String levelName(int level) {
        switch (level) {
            case VERBOSE:
                return "VERBOSE";
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case WARN:
                return "WARN";
            case ERROR:
                return "ERROR";
            default:
                return "DEFAULT";
        }
    }



    public static void registerUncaughtExceptionHandler() {
        final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable ex) {

//                exitApp();

                dumpToSDCard(thread,ex);
//                printError(ERROR, GLOBAL, "uncaughtException", ex, true);
                defaultUncaughtExceptionHandler.uncaughtException(thread, ex);
            }
        });
    }

    public static void filePrintError(int level, String tag, String msg, Throwable t) {
        executorService.submit(new FilePrintRunnableError(level, tag, msg, t));
    }

    public static void filePrintLog(int level, String tag, String msg, Throwable t) {
        executorService.submit(new FilePrintRunnableLog(level, tag, msg, t));
    }

    private static void dumpToSDCard(final Thread t, final Throwable e) {
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Log.i("GXW", "no sdcard skip dump ");
            return;
        }

        String mLodPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/crashHandler/";
        File file = new File(mLodPath);
        if (!file.exists()) {
            file.mkdirs();
        }

        String time = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.CHINA).format(new Date(System.currentTimeMillis()));
        Log.i("GXW", mLodPath + time + ".trace");
        File logFile = new File(mLodPath, time + ".trace");

        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(logFile)));
            pw.println(time);
//            dumpPhoneInfo(pw, TMapApplication.getAppContext());
            pw.println();
            e.printStackTrace(pw);
            pw.close();

        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    private static void dumpPhoneInfo(PrintWriter pw, Context mContext) {
        //应用的版本名称和版本号
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = null;
        try {
            pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                pw.print("App Version: ");
                pw.print(pi.versionName);
                pw.print('_');
                pw.println(pi.versionCode);

                //android版本号
                pw.print("OS Version: ");
                pw.print(Build.VERSION.RELEASE);
                pw.print("_");
                pw.println(Build.VERSION.SDK_INT);

                //手机制造商
                pw.print("Vendor: ");
                pw.println(Build.MANUFACTURER);

                //手机型号
                pw.print("Model: ");
                pw.println(Build.MODEL);

                //cpu架构
                pw.print("CPU ABI: ");
                pw.println(Build.CPU_ABI);
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }


}
